﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace Stanza.Silverlight.Media
{
    //
    // Calculations from:
    //   http://ilab.usc.edu/wiki/index.php/HSV_And_H2SV_Color_Space
    //

    public struct HSVColor
    {
        public const double Zero = 0.0;
        public const double Degrees = 360.0;
        public const double MinPercent = 0.0;
        public const double MaxPercent = 100.0;

        private const byte Opaque = 0xFF;

        private double h, s, v;

        public HSVColor( double hue, double saturation, double value )
        {
            h = NormalizeHue( hue );
            s = NormalizePercent( saturation );
            v = NormalizePercent( value );
        }

        private static double NormalizeHue( double value )
        {
            while( value < 0.0 )
                value += Degrees;
            while( value >= Degrees )
                value -= Degrees;
            return value;
        }
        private static double NormalizePercent( double value )
        {
            if( value > MaxPercent )
                value = MaxPercent;
            else if( value < MinPercent )
                value = MinPercent;
            return value;
        }

        public double Hue
        {
            get { return h; }
            set { h = NormalizeHue( value ); }
        }

        public double Saturation
        {
            get { return s; }
            set { s = NormalizePercent( value ); }
        }

        public double Value
        {
            get { return v; }
            set { v = NormalizePercent( value ); }
        }

        private static byte ToByte( double percent )
        {
            return (byte)( 255 * percent );
        }

        private static double ToPercent( byte byteValue )
        {
            return ( byteValue / 255.0 );
        }

        public static Color ToColor( HSVColor hsvColor )
        {
            double S = hsvColor.Saturation / MaxPercent;
            double V = hsvColor.Value / MaxPercent;

            Color rgbColor = Colors.Black;
            if( V != 0.0 )
                if( S == 0.0 )
                {
                    byte grayValue = ToByte( V );
                    rgbColor = new Color { A = 0xFF, R = grayValue, G = grayValue, B = grayValue };
                }
                else
                {
                    double hf = hsvColor.Hue / 60.0;
                    double f = hf - Math.Floor( hf );
                    byte vv = ToByte( V );
                    byte pv = ToByte( V * ( 1 - S ) );
                    byte qv = ToByte( V * ( 1 - S * f ) );
                    byte tv = ToByte( V * ( 1 - S * ( 1 - f ) ) );
                    switch( (int)hf % 6 )
                    {
                    case 0: rgbColor = new Color { A = 0xFF, R = vv, G = tv, B = pv }; break;
                    case 1: rgbColor = new Color { A = 0xFF, R = qv, G = vv, B = pv }; break;
                    case 2: rgbColor = new Color { A = 0xFF, R = pv, G = vv, B = tv }; break;
                    case 3: rgbColor = new Color { A = 0xFF, R = pv, G = qv, B = vv }; break;
                    case 4: rgbColor = new Color { A = 0xFF, R = tv, G = pv, B = vv }; break;
                    case 5: rgbColor = new Color { A = 0xFF, R = vv, G = pv, B = qv }; break;
                    }
                }
            return rgbColor;
        }

        public static HSVColor FromColor( Color rgbColor )
        {
            double R = ToPercent( rgbColor.R );
            double G = ToPercent( rgbColor.G );
            double B = ToPercent( rgbColor.B );

            double H = Zero;
            double S = Zero;
            double V = Zero;

            if( ( B > G ) && ( B > R ) )
            {
                V = B;
                if( V != Zero )
                {
                    double min = ( R > G ) ? G : R;
                    double delta = V - min;
                    if( delta != Zero )
                    {
                        S = delta / V;
                        H = 4 + ( R - G ) / delta;
                    }
                    else
                        H = 4 + ( R - G );
                }
            }
            else if( G > R )
            {
                V = G;
                if( V != Zero )
                {
                    double min = ( R > B ) ? B : R;
                    double delta = V - min;
                    if( delta != Zero )
                    {
                        S = delta / V;
                        H = 2 + ( B - R ) / delta;
                    }
                    else
                        H = 2 + ( B - R );
                }
            }
            else
            {
                V = R;
                if( V != Zero )
                {
                    double min = ( G > B ) ? B : G;
                    double delta = V - min;
                    if( delta != Zero )
                    {
                        S = delta / V;
                        H = ( G - B ) / delta;
                    }
                    else
                        H = ( G - B );
                }
            }

            return new HSVColor( H * 60, S * MaxPercent, V * MaxPercent );
        }

        public static Color GetColorFromHue( double hue )
        {
            return GetColorFromHue( hue, Opaque );
        }

        public static Color GetColorFromHue( double hue, byte alpha )
        {
            const int HueBands = 6;
            const double HueBandCount = Degrees / HueBands;

            hue = NormalizeHue( hue );

            byte modValue = (byte)( ( hue % HueBandCount ) * HueBands );
            byte diffValue = (byte)( Byte.MaxValue - modValue );
            Color colorValue = Colors.Black;
            switch( (int)( hue / HueBandCount ) % HueBands )
            {
            case 0: colorValue = Color.FromArgb( alpha, Byte.MaxValue, modValue, Byte.MinValue ); break;
            case 1: colorValue = Color.FromArgb( alpha, diffValue, Byte.MaxValue, Byte.MinValue ); break;
            case 2: colorValue = Color.FromArgb( alpha, Byte.MinValue, Byte.MaxValue, modValue ); break;
            case 3: colorValue = Color.FromArgb( alpha, Byte.MinValue, diffValue, Byte.MaxValue ); break;
            case 4: colorValue = Color.FromArgb( alpha, modValue, Byte.MinValue, Byte.MaxValue ); break;
            case 5: colorValue = Color.FromArgb( alpha, Byte.MaxValue, Byte.MinValue, diffValue ); break;
            }
            return colorValue;
        }
    }
}
